﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BestPractices
{
    public abstract class ObjectPool
    {
        private int expirationTime;
        private int cleanupThreadSleepTime;
        private int lastCheckOut;
        private Hashtable locked;
        private Hashtable unlocked;
        private CleanUpThread cleaner;

        public ObjectPool()
        {
            this.expirationTime = 5000;
            this.cleanupThreadSleepTime = 5000;
            this.locked = new Hashtable();
            this.unlocked = new Hashtable();

            this.lastCheckOut = DateTime.Now.Millisecond;

            this.cleaner = new CleanUpThread(this, this.expirationTime);

            Thread cleanerThread = new Thread(cleaner.run);

            cleanerThread.IsBackground = true;
            cleanerThread.Start();
        }

        public abstract Object Create();

        public abstract bool Validate(Object paramObject);

        public abstract void Expire(Object paramObject);

        public Object CheckOut()
        {
            int l = System.DateTime.Now.Millisecond;
            this.lastCheckOut = l;

            Object localObject = Create();

            if (this.unlocked.Count > 0)
            {
                IEnumerator localEnumeration = this.unlocked.Keys.GetEnumerator();

                while (localEnumeration.MoveNext())
                {

                    if (Validate(localObject))
                    {
                        this.unlocked.Remove(localObject);
                        this.locked.Add(localObject, l);
                        return localObject;
                    }

                    this.unlocked.Remove(localObject);
                    Expire(localObject);
                    localObject = null;
                }

            }

            this.locked.Add(localObject, l);
            return localObject;
        }

        public void CheckIn(Object paramObject)
        {
            if (paramObject != null)
            {
                this.locked.Remove(paramObject);
                this.unlocked.Add(paramObject, System.DateTime.Now.Millisecond);
            }
        }

        public void CleanUp()
        {
            long l = System.DateTime.Now.Millisecond;

            IEnumerator localEnumeration = this.unlocked.Keys.GetEnumerator();

            while (localEnumeration.MoveNext())
            {
                Object localObject = localEnumeration.Current;

                if (l - (long)this.unlocked[localObject] > this.expirationTime)
                {
                    this.unlocked.Remove(localObject);
                    Expire(localObject);
                    localObject = null;
                }
            }
        }
    }

}
